package middleware

import (
	"errors"
	"jtvss/api/system/model"
	"jtvss/pkg/ctx"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

// UserClaims 用户Claims

type jwtClaims struct {
	User model.Token
	jwt.StandardClaims
}

// JWT 定义
type JWT struct {
	SigningKey []byte
}

var (
	ErrTokenMalformed    error = errors.New("token not event")
	ErrTokenExpired      error = errors.New("token expired")
	ErrTokenNotValidYet  error = errors.New("token not valid yet")
	ErrTokenInValidation error = errors.New("token invalidation")
)

func signKey() string {
	return "jtvss"
}

// NewJWT 创建JWT
func NewJWT() *JWT {
	return &JWT{
		[]byte(signKey()),
	}
}

// createToken 创建Token
func (j *JWT) createToken(claims jwtClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

// ParseToken 解析Token
func (j *JWT) ParseToken(tokenString string) (*jwtClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorClaimsInvalid != 0 {
				return nil, ErrTokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, ErrTokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, ErrTokenNotValidYet
			} else {
				return nil, ErrTokenInValidation
			}
		}
	}

	if claims, ok := token.Claims.(*jwtClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, ErrTokenInValidation
}

// RefreshToken 刷新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		return "", err
	}

	if claims, ok := token.Claims.(*jwtClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.createToken(*claims)
	}
	return "", ErrTokenInValidation
}

// GenerateToken 生成Token
func GenerateToken(token *model.Token) (string, error) {
	j := NewJWT()
	claims := jwtClaims{}
	claims.User = *token
	claims.StandardClaims = jwt.StandardClaims{
		NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间
		ExpiresAt: int64(time.Now().Unix() + 3600), // 过期时间 一小时
		Issuer:    string(j.SigningKey),            // 签名的发行者
	}
	return j.createToken(claims)
}

// UseJWTAuth 鉴权
func UseJWTAuth(r *gin.RouterGroup) *gin.RouterGroup {
	r.Use(func(c *gin.Context) {
		// jwt鉴权取头部信息 x-token
		// 登录时回返回token信息
		// 前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
		token := c.Request.Header.Get("Token")
		j := NewJWT()
		claims, err := j.ParseToken(token)
		if err != nil {
			// if err == middleware.TokenValidationErrorExpired {
			// 	c.Abort()
			// 	return
			// }
			ctx.JSON(ctx.StatusLoginExpired).SetMsg(err.Error()).WriteData(nil, c)
			c.Abort()
			return
		}
		// j.RefreshToken(token)
		c.Set("claims", claims)
		c.Next()
	})
	return r
}

type token struct {
	Token string `json:"token" form.:"token"`
}

func UseTokenAuth(r *gin.RouterGroup) *gin.RouterGroup {
	r.Use(func(c *gin.Context) {
		// data token
		var p token
		c.ShouldBind(&p)
		token := strings.TrimPrefix(p.Token, "Bearer ")
		claims, err := NewJWT().ParseToken(token)
		if err != nil {
			// if err == middleware.TokenValidationErrorExpired {
			// 	c.Abort()
			// 	return
			// }
			ctx.JSON(ctx.StatusLoginExpired).SetMsg(err.Error()).WriteData(nil, c)
			c.Abort()
			return
		}
		c.Set("claims", claims)
		c.Next()
	})
	return r
}

// UserToken 根据Token获取用户信息
func UserToken(c *gin.Context) *model.Token {
	claims, _ := c.Get("claims")
	if claims == nil {
		return &model.Token{}
	}
	return &claims.(*jwtClaims).User
}
